package com.starryx.websocket.chatroom.net.handler;

import com.starryx.websocket.chatroom.net.constant.MessageType;
import com.starryx.websocket.chatroom.net.session.Session;
import com.starryx.websocket.chatroom.utils.SpringContextUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.regex.Pattern;

public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private static final Logger log = LogManager.getLogger(TextWebSocketFrameHandler.class);

    // 可以通过ChannelGroup可管理服务器端所有的连接的Channel，然后对所有的连接Channel广播消息
    private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private Session session = SpringContextUtil.getBean(Session.class);

    /**
     * 消息格式正则表达式
     */
    private static final Pattern MSG_PETTERN = Pattern.compile("^(LOGIN|MSG):(.)+$");

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 记录当前发送信息的 channel
        Channel curChannel = ctx.channel();
        // 判断是否符合消息的格式
        if (!MSG_PETTERN.matcher(msg.text()).matches()) {
            // 不符合立即返回
            return;
        }
        // 获取文本内容
        String[] text = msg.text().split(":");
        /*
        * 预定好客户端传来的消息为 “消息类型:消息内容”
        * 登录消息:用户名（默认不可重复）
        * 发送消息:消息内容
        */
        String type = text[0];
        String content = text[1];
        if (type.equals(MessageType.LOGIN)) {
            if (session.isLogin(content)) {
                curChannel.writeAndFlush(new TextWebSocketFrame("您已经登录了"));
                return;
            }
            // 将用户名和所在 channel 存入 session 中
            session.put(content, curChannel);
            // 将 channel 加入 ChannelGroup 中，用于之后广播消息
            channels.add(curChannel);
            // 广播消息
            channels.writeAndFlush(new TextWebSocketFrame("[" + content + "] - 加入群聊"));
        } else if (type.equals(MessageType.MSG)) {
            // 先检查 session 中是否存在
            if (!session.isLogin(curChannel)) {
                curChannel.writeAndFlush(new TextWebSocketFrame("您还没有登录了"));
                return;
            }
            // 遍历所有已连接的 channel
            for (Channel channel : channels) {
                // 根据是否为自己的 channel 广播不同的消息
                if (channel != curChannel) {
                    channel.writeAndFlush(new TextWebSocketFrame("[" + session.getName(curChannel) + "]" + content));
                } else {
                    channel.writeAndFlush(new TextWebSocketFrame("[自己]" + content));
                }
            }
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel curChannel = ctx.channel();
        // 如果断开连接的 channel 不是已登录用户的 channel 就略过
        if (!session.isLogin(curChannel)) {
            return;
        }
        String name = session.getName(curChannel);
        channels.writeAndFlush(new TextWebSocketFrame("[" + name + "] - 离开群聊"));
        channels.remove(curChannel);
        session.remove(curChannel);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        Channel curChannel = ctx.channel();
        log.error("Client： {} 异常", curChannel.remoteAddress());
        log.error(cause.getMessage());
        // 当出现异常就关闭连接
        ctx.close();
    }
}
